package com.redis.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.util.*;

/**
 * @author H
 * @date 2023/1/3
 */
@RestController
public class TestRedisController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("stringAndHash")
    public String testStringAndHash() {
        // redisTemplate存入的key和value将会是序列化后的数据：\xac\xed\x00\x05t\x00\x011
        redisTemplate.opsForValue().set("key1", "value1");
        redisTemplate.opsForValue().set("int_key", "1");
        stringRedisTemplate.opsForValue().set("int", "1");
        // 使用运算
        stringRedisTemplate.opsForValue().increment("int", 1);
        // 获取底层Jedis连接
        Jedis jedis = (Jedis) Objects.requireNonNull(stringRedisTemplate.getConnectionFactory()).getConnection().getNativeConnection();
        // 减1操作，这个命令RedisTemplate不支持，所以先获取底层的连接再操作
        jedis.decr("int");
        Map<String, String> map = new HashMap<>();
        map.put("field1", "value1");
        map.put("field2", "value2");
        // 存入一个散列数据类型
        stringRedisTemplate.opsForHash().putAll("hash", map);
        // 新增一个字段
        stringRedisTemplate.opsForHash().put("hash", "field3", "field3");
        // 绑定散列操作的key,这样可以连续对同一个散列数据类型进行操作
        BoundHashOperations<String, Object, Object> hashOps = stringRedisTemplate.boundHashOps("hash");
        // 删除两个字段
        hashOps.delete("field1", "field2");
        // 新增一个字段
        hashOps.put("field4", "value5");

        return "success";
    }

    /**
     * 在同一个连接下执行多条Redis命令
     *
     * @return String
     */
    @GetMapping("sessionCallback")
    public String testSessionCallback() {
        stringRedisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                operations.opsForValue().set("key1", "value1");
                operations.opsForHash().put("hash", "field", "hvalue");
                return null;
            }
        });

        return "success";
    }

    /**
     * 使用redis流水线测试性能
     * 批量送给redis服务器命令
     *
     * @return String
     */
    @GetMapping("pipLine")
    public String testPipLine() {
        long start = System.currentTimeMillis();
        stringRedisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                for (int i = 1; i <= 10000; i++) {
                    operations.opsForValue().set("pipeline_" + i, "value_" + i);
                    String value = (String) operations.opsForValue().get("pipeline_" + i);
                    if (i == 10000) {
                        System.out.println("命令只是进入队列，所以值为空[" + value + "]");
                    }
                }
                return null;
            }
        });
        long end = System.currentTimeMillis();
        System.out.println("耗时：" + (end - start) + "毫秒");

        return "success";
    }


    /**
     * 测试redis发布订阅
     *
     * @return String
     */
    @GetMapping("redisMessageListener")
    public String testRedisMessageListener() {
        stringRedisTemplate.convertAndSend("topic1", "发布一个消息");
        return "success";
    }

    /**
     * 测试lua脚本
     *
     * @return String
     */
    @GetMapping("lua")
    public String testLua() {
        DefaultRedisScript<String> script = new DefaultRedisScript<>();
        // 设置脚本
        script.setScriptText("return 'Hello Redis'");
        // 定义返回类型，注意：如果没有这个定义，Spring不会返回结果
        script.setResultType(String.class);
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        // 执行lua脚本
        // String str = (String) redisTemplate.execute(script, stringSerializer, stringSerializer, null);
        String str = stringRedisTemplate.execute(script, null);

        return str;
    }

    /**
     * 测试lua脚本
     * redis.call('set', KEYS[1], ARGV[1])
     * redis.call('set', KEYS[2], ARGV[2])
     * local str1 = redis.call('get', KEYS[1])
     * local str2 = redis.call('get', KEYS[2])
     * if str1 == str2 then
     * return 1
     * end
     * return 0
     *
     * @return String
     */
    @GetMapping("luaOfArgs")
    public String testLuaOfArgs() {
        // 定义lua脚本
        String lua = "redis.call('set', KEYS[1], ARGV[1])\n" +
                "        redis.call('set', KEYS[2], ARGV[2])\n" +
                "        local str1 = redis.call('get', KEYS[1])\n" +
                "        local str2 = redis.call('get', KEYS[2])\n" +
                "        if str1 == str2 then\n" +
                "        return 1\n" +
                "        end \n" +
                "        return 0";
        // 结果返回Long
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(lua);
        script.setResultType(Long.class);
        // 定义key参数
        List<String> keyList = new ArrayList<>();
        keyList.add("key1");
        keyList.add("key2");
        // 传递两个参数值
        String value1 = "nihao1", value2 = "nihao";
        Long result = stringRedisTemplate.execute(script, keyList, value1, value2);
        return String.valueOf(result);
    }

}
